package com.me.study.config;

import com.me.study.beanPostProcessor.TestStaticPriority;
import com.me.study.dao.IndexDao;
import com.me.study.dao.IndexDaoImpl2;
import com.me.study.importSelector.EnableSelector;
import com.me.study.service.IndexService;
import com.me.study.service.IndexServiceImpl2;
import com.me.study.simpleMapperScan.MyMapperScan;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.ClassPathResource;

/**
 * 
 *
 * @author ME
 * @date   2019/12/14
 */
@Configuration
@ComponentScan("com.me.study")
// 简化源码调试，采用以下配置，只注册 dao 和 service
// @ComponentScan(value = "com.me.study",
// 	excludeFilters = {
// 		@Filter(type = FilterType.CUSTOM, classes = {MyExcludeTypeFilter.class}),
// 		@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {IndexDao.class}),
// 		@Filter(type = FilterType.ASPECTJ, pattern = {"com.me.study.dao.*", "com.me.study.service.*})
// 	}
// )
@MyMapperScan(basePackages = "配置了要扫描的包路径")
@EnableSelector(isEnable = true)
@Import(AppConfig.TestImport.class)
public class AppConfig {

	// @Bean
	// public static TestFactoryBean hhFactoryBean() {
	// 	return new TestFactoryBean();
	// }

	/**
	 * TODO `@Bean` 标注的普通 bean方法不能带 static，否则会调用 2次！！为什么 BeanPostProcessors 不会？？？？？
	 *  猜测是 Spring 对 BeanPostProcessors 特殊处理了
	 *
	 */
	@Bean
	public IndexDao indexDaoImpl2() {
		// 源码见：ConfigurationClassPostProcessor.enhanceConfigurationClasses
		System.out.println("Construct：[IndexDaoImpl2] 通过调用 @Bean方法 从spring容器中获取 bean 时，通过 CGLIB代理 实现从 bean工厂中获取单例的 bean。IndexDaoImpl2 Constructor");
		return new IndexDaoImpl2();
	}

	@Bean
	public IndexService indexServiceImpl2() {
		IndexServiceImpl2 service = new IndexServiceImpl2();
		service.setDao(indexDaoImpl2());
		return service;
	}

	/**
	 * MapperScannerConfigurer 和 PropertyPlaceholderConfigurer 之类的 Bean 需要在其他 Bean初始化之前完成
	 * （比如: 其他 bean需要 PropertyPlaceholderConfigurer替换变量符 "${express}"）,<p>
	 * 所以必须要标记为 static方法，以示优先加载, 否则会给出警告。<p></p>
	 *
	 * 警告: @Bean method AppConfig.propertyPlaceholderConfigurer is non-static and returns an
	 * object assignable to Spring's BeanFactoryPostProcessor interface.
	 * This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct
	 * within the method's declaring @Configuration class. Add the 'static' modifier to
	 * this method to avoid these container lifecycle issues;
	 * see @Bean javadoc for complete details.
	 */
	@Bean
	public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
		PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
		configurer.setLocation(new ClassPathResource("common.conf"));
		return configurer;
	}

	/**
	 * 如果没有 static 关键字，会提示：<p>
	 * 		Bean 'appConfig' of type [*] is not eligible for getting processed by all BeanPostProcessors	<p>
	 * 见：https://mp.weixin.qq.com/s/VTB9f8S6TYeuNVd4Z71zUw		<p></p>
	 *
	 * 因为 BeanPostProcessor 是用来增强 bean的，需要先初始化。而在普通 bean里配置非 static的 BeanPostProcessor 会被动的提前初始化外部 bean
	 */
	@Bean
	public static TestStaticPriority testStaticPriority() {
		return new TestStaticPriority();
	}

	public static class TestImport {
		public TestImport() {
			System.out.println("【Spring】 @Import");
		}
	}

}
